use std::fmt::{Debug, Display};

use crate::gen_title;

pub fn ch2_19_1() {
    gen_title("ch2_19_1", 18);

    #[derive(Debug)]
    enum UiObject {
        Button,
        SelectBox,
    }

    let objects = [UiObject::Button, UiObject::SelectBox];

    for o in objects {
        draw(o);
    }

    fn draw(o: UiObject) {
        println!("{:?}", o);
    }
}

pub fn ch2_19_2() {
    gen_title("ch2_19_2", 18);

    trait Draw: Debug {
        fn draw(&self);

        fn height(&self) -> u32;

        fn width(&self) -> u32;
    }

    #[derive(Debug)]
    struct Button {
        width: u32,
        height: u32,
        label: String,
    }

    impl Draw for Button {
        fn draw(&self) {
            println!("draw button");
        }

        fn height(&self) -> u32 {
            self.height
        }

        fn width(&self) -> u32 {
            self.width
        }
    }

    #[derive(Debug)]
    struct SelectBox {
        width: u32,
        height: u32,
        options: Vec<String>,
    }

    impl Draw for SelectBox {
        fn draw(&self) {
            println!("draw selectbox");
        }

        fn height(&self) -> u32 {
            self.height
        }

        fn width(&self) -> u32 {
            self.width
        }
    }

    #[derive(Debug)]
    struct Screen {
        components: Vec<Box<dyn Draw>>,
    }

    impl Screen {
        pub fn run(&self) {
            for component in self.components.iter() {
                component.draw();
            }
        }
    }

    let screen = Screen {
        components: vec![
            Box::new(Button {
                width: 100,
                height: 100,
                label: "button".to_string(),
            }),
            Box::new(SelectBox {
                width: 100,
                height: 100,
                options: vec![
                    "option1".to_string(),
                    "option2".to_string(),
                    "option3".to_string(),
                ],
            }),
        ],
    };

    screen.run();

    println!("{:?}", screen);
}

pub fn ch2_19_3() {
    gen_title("ch2_19", 18);

    trait Draw {
        fn draw(&self) -> String;
    }

    impl Draw for u8 {
        fn draw(&self) -> String {
            format!("u8: {}", self)
        }
    }

    impl Draw for f64 {
        fn draw(&self) -> String {
            format!("f64: {}", self)
        }
    }

    fn draw1(x: Box<dyn Draw>) {
        let result = x.draw();
        println!("{}", result);
    }

    fn draw2(x: &dyn Draw) {
        let result = x.draw();
        println!("{}", result);
    }

    let x = 1.1f64;
    let y = 8u8;

    draw1(Box::new(x));
    draw1(Box::new(y));
    draw2(&x);
    draw2(&y);
}

pub fn ch2_19_4() {
    gen_title("ch2_19_4", 18);

    trait Draw {
        fn draw(&self) -> Self;
    }

    #[derive(Clone, Debug)]
    struct Button;

    impl Draw for Button {
        fn draw(&self) -> Self {
            return self.clone();
        }
    }

    let button = Button;
    let newb = button.draw();
    println!("{:?}", newb);
}

pub fn ch2_19_pra_1() {
    gen_title("ch2_19_pra_1", 18);

    trait Bird {
        fn quack(&self) -> String;
    }

    struct Duck;

    impl Duck {
        fn swim(&self) {
            println!("Look, the duck is swimming");
        }
    }

    struct Swan;

    impl Swan {
        fn fly(&self) {
            println!("Look, the duck.. oh sorry, the swan is flying");
        }
    }

    impl Bird for Duck {
        fn quack(&self) -> String {
            "duck duck".to_string()
        }
    }

    impl Bird for Swan {
        fn quack(&self) -> String {
            "swan swan".to_string()
        }
    }

    let duck = Duck;
    duck.swim();

    let bird = hatch_a_bird(2);
    assert_eq!(bird.quack(), "duck duck");

    let bird = hatch_a_bird(1);
    assert_eq!(bird.quack(), "swan swan");

    println!("Success!");

    fn hatch_a_bird(x: i32) -> Box<dyn Bird> {
        if x == 2 {
            Box::new(Duck)
        } else {
            Box::new(Swan)
        }
    }
}

pub fn ch2_19_pra_2() {
    gen_title("ch2_19_pra_2", 18);

    trait Bird {
        fn quack(&self);
    }

    struct Duck;

    impl Duck {
        fn fly(&self) {
            println!("Look, the duck is flying");
        }
    }

    struct Swan;

    impl Swan {
        fn fly(&self) {
            println!("Look, the duck.. oh sorry, the swan is flying");
        }
    }

    impl Bird for Duck {
        fn quack(&self) {
            println!("duck duck");
        }
    }

    impl Bird for Swan {
        fn quack(&self) {
            println!("swan swan");
        }
    }

    let birds: [Box<dyn Bird>; 2] = [Box::new(Duck), Box::new(Swan)];

    for bird in birds {
        bird.quack();
    }
}

pub fn ch2_19_pra_3() {
    gen_title("ch2_19_pra_3", 18);

    trait Draw {
        fn draw(&self) -> String;
    }

    impl Draw for u8 {
        fn draw(&self) -> String {
            format!("u8: {}", self)
        }
    }

    impl Draw for f64 {
        fn draw(&self) -> String {
            format!("f64: {}", self)
        }
    }

    let x = 1.1f64;
    let y = 8u8;

    draw_with_box(Box::new(x));

    draw_with_ref(&y);

    println!("success");

    fn draw_with_box(x: Box<dyn Draw>) {
        println!("{}", x.draw());
    }

    fn draw_with_ref(x: &dyn Draw) {
        println!("{}", x.draw());
    }
}

pub fn ch2_19_pra_4() {
    gen_title("ch2_19_pra_4", 18);

    trait Foo {
        fn method(&self) -> String;
    }

    impl Foo for u8 {
        fn method(&self) -> String {
            format!("u8: {}", self)
        }
    }

    impl Foo for String {
        fn method(&self) -> String {
            format!("String: {}", self)
        }
    }

    fn static_dispatch<T: Foo>(x: T) {
        println!("{}", x.method());
    }

    fn dynamic_dispatch(x: &dyn Foo) {
        println!("{}", x.method());
    }

    let x = 5u8;
    let y = "Hello".to_string();

    static_dispatch(x);
    dynamic_dispatch(&y);

    println!("success");
}

pub fn ch2_19_pra_5() {
    gen_title("ch2_19_pra_5", 18);

    trait MyTrait {
        fn f(&self) -> Box<dyn MyTrait>;
    }

    impl MyTrait for u32 {
        fn f(&self) -> Box<dyn MyTrait> {
            Box::new(42)
        }
    }

    impl MyTrait for String {
        fn f(&self) -> Box<dyn MyTrait> {
            Box::new(self.clone())
        }
    }

    fn my_function(x: Box<dyn MyTrait>) -> Box<dyn MyTrait> {
        x.f()
    }

    println!("Success");
}
